(function() {
	var K = {
			global : (function() { return this; }())
		},
		
		defaults = {
			basePath : "./"
		},		
		
		merge = function(o1, o2) {
			for (var a in o1) {
				if (o1.hasOwnProperty(a)) {
					o2[a] = o1[a];
				}
			}
		},
		
		loader = (function () {
			var counter = 0,
			
				track = (function() {
					var func,
						ua = navigator.userAgent;
					
					return function(n, c) {
						func = func || (function(){
							switch (true) {
								case (function(m) {return Boolean(m&&m[1]); })(m=ua.match(/MSIE\s([^;]*)/)) :
									return function(n, c) {
										var hasRun = false,
											run = function() {
												if (!hasRun) {
													hasRun = true;
													c();
												}
											},
											
											check = function(rs) {
				                                if ("loaded" === rs || "complete" === rs) {
				                                    run();
				                                }
											};
			
										check(n.readyState);
										n.onreadystatechange = function() { check(this.readyState); };
									};
								
								case (Boolean(/KHTML/.test(ua))) : 
									return function(n, c) {
			                            var hasRun = false,
			                            	run = function() {
				                                if (!hasRun) {
				                                    hasRun = true;
				                                    c();
				                                }
				                            };
			                            
										n.addEventListener("load", function() { run(); }, false);			
									};
								
								default : 
									return function(n, c) {
			                            var hasRun = false,
			                            	run = function() {
				                                if (!hasRun) {
				                                    hasRun = true;
				                                    c();
				                                }
				                            };			
			                            
			                            n.onload = function() { run(); };
			                            n.onerror = function() { throw "Error while loading file \"" + n.src + "\"" };
									};
							}
						}());
						
					return func(n, c);
					}
				}()),
			
				normalizePath = function(s) {
					var n = document.createElement("script");
					n.src = s;
					try {
			    		return n.src;
					} finally {
						n = null;
					}
				};
			
			return {
				isLoaded : function(src) {
					var s = document.getElementsByTagName("script");
					src = normalizePath(src);
					for (var i = s.length - 1; i > -1; i -= 1) {
						if (s[i].src === src) {
							return true;
						}
					}
					
					return false;
				},
				
				load : function(src, callback) {
					K.asap(function() {
						var d = document,
							n = d.createElement("script"),
							h = d.getElementsByTagName("head")[0];
						
						n.id = "K.loader.id_" + (counter++);
						
						if (callback) {
							track(n, callback);
						}
						
						n.src = src;
						h.appendChild(n);
					});
				}
			};
		}());
		
	K.global.K = K;

	merge(K, {
		config : (function() {
			var configured = false;
			
			return function(options) {
				if (configured) {
					throw "Cannot reconfigure";
				}
				
				merge(options, defaults);
			};		
		}()),
		
		data : (function() {
			var data = {},
				
				dataFunction = function() {
					switch (arguments.length) {
						case 1: 
							return data[arguments[0]];
							
						case 2: 
							return void (data[arguments[0]] = arguments[1]);
							
						default: 
							throw "Invalid number of arguments";
					}
				};
			
			dataFunction.containsKey = function(key) {
				return data.hasOwnProperty(key);
			};
			
			dataFunction.remove = function(key) {
				return delete data[key];
			};
			
			return dataFunction;
		}()),
		
		asap : (function() {
			var ready = false,
				functions = [],
				readyBound = false,
				
				go = function() {
					if (!ready) { 
			    		ready = true;
			    		for (var i = 0, l = functions.length; i < l; i++) {
			    			functions[i]();
			    		}
					}
				},
				
				bind = function() {
					if (readyBound) {
						return;
					}
					
					readyBound = true;
					
					if (doc.addEventListener) {
						doc.addEventListener("DOMContentLoaded", function() {
							doc.removeEventListener( "DOMContentLoaded", arguments.callee, false);
							go();
						}, false);
						doc.addEventListener("load", function() {
			                doc.removeEventListener( "load", arguments.callee, false);
			                go();
			            }, false)
					} else if (doc.attachEvent) {
						doc.attachEvent("onreadystatechange", function(){
							if (doc.readyState === "complete") {
								doc.detachEvent( "onreadystatechange", arguments.callee);
								go();
							}
						});
				
						if (doc.documentElement.doScroll && window == window.top) {
							(function(){
								if (ready) {
									return;
								}
					
								try {
									doc.documentElement.doScroll("left");
								} catch(e) {
									setTimeout(arguments.callee, 0);
									return;
								}
								go();
							})();
						}
					}
					
					if ((/KHTML/).test(navigator.userAgent)) {
						(function() {
			                if (/loaded|complete/.test(doc.readyState)) {
			                	go();
			                } else {
			                    setTimeout(arguments.callee, 0);
			                }
						}());
			        }
					
					var previousOnload = onload;
					onload = function() {
						go();
						if (previousOnload) {
			    			previousOnload.apply(this, Array.prototype.slice.call(arguments));
						}
					};
				};	
			
			bind();
			
			return function(f) {
				if (ready) {
					return void f();
				}
				
				functions.push(f);
				bind();
			};
		}()),
		
		classLoader : (function() {
			
			var ClassLoader = function(basePath) {
				var classPayloads = {},
					payloads = {},
					payloadCounter = 0,
			    	scripts = {},
					scope = {},
					
					Loader = function () {
				    	var payload = new Payload();
				    	
				    	return {
				    		requires : function(className, importName) {
						    	payload.requires(className, importName);
						    	return this;
				    		},
				    		
				    		provides : function(className) {
				    			payload.provides(className);
						    	return this;
						    },
				
						    includes : function(className, importName) {
						    	payload.includes(className, importName);
						    	return this;
						    },	
						    
						    requireScript : function(scriptName) {
						    	payload.requireScript(scriptName);
						    	return this;
						    },
						    
						    includeScript : function(scriptName) {
						    	payload.includeScript(scriptName);
						    },
						    
			                load : function(func) {
			                    payload.load(func);
			                }
				    	};
				    },
				    
				    Payload = (function() {
				    	var Payload = function() {
					    		this.id = null;
					    		this.reqs = {};
					    		this.incs = {};
					    		this.clazz = null;
					    		this.imports = {};
					    		this.payload = null;
					    		this.reference = null;
					    		this.run = false;
					    		this.reqScripts = {};
					    		this.incScripts = {};
							},
							
							p = Payload.prototype;
					
						p.requires = function(className, importName) {
					    	this.reqs[className] = importName;
						};
						
						p.provides = function(className) {
							this.clazz = className;
					    };
				
					    p.includes = function(className, importName) {
					    	this.incs[className] = importName;
					    };	
					    
						p.load = function(func) {
							this.payload = func;
					    	handlePayload(this);
					    };
					    
					    p.requireScript = function(scriptName) {
					    	this.reqScripts[scriptName] = true;
					    };
					    
					    p.includeScript = function(scriptName) {
					    	this.incScripts[scriptName] = true;
					    };
					    
				    	return Payload;
				    }()),
			    
					getClassFile = function (className) {
				        return basePath + className.replace(/\./g, "/") + ".js";		
					},
				
			    	checkDependencies = function (payload) {
				    	var clazz = payload.clazz;
				        var checkedClasses = {};
				        
				        (function(payload) {
				        	var func = arguments.callee;
				        	var reqs = payload.reqs;
				        	for (var c in reqs) {
				        		if (reqs.hasOwnProperty(c) && classPayloads.hasOwnProperty(c) && !(checkedClasses.hasOwnProperty(c))) {
				        			checkedClasses[c] = null;
				        			var cp = classPayloads[c];
				        			if (cp.reqs.hasOwnProperty(clazz)) {
				                        throw "Cycle detected within class \"" + cp.clazz + "  \" while loading class \"" + clazz + "\"";
				        			}
				        			func(cp);
				        		}
				        	}
				        	
				        	var incs = payload.incs;
				        	for (var c in incs) {
				                if (incs.hasOwnProperty(c) && classPayloads.hasOwnProperty(c) && !(checkedClasses.hasOwnProperty(c))) {
				                    checkedClasses[c] = null;
				                    var cp = classPayloads[c];
				                    if (cp.reqs.hasOwnProperty(p)) {
				                        throw "Cycle detected within class \"" + cp.clazz + "  \" while loading class \"" + clazz + "\"";
				                    }
				                    
				                    arguments.callee(cp);
				                }        		
				        	}
				        })(payload);
				        
				    },
				
			    	handlePayload = function(payload) {
				    	populateImportNames(payload);
				    	
						if (payload.clazz) {
							processClassPayload(payload);
						} else {
							processPayload(payload);
						}
					},
				
					fillImportNames = function(from, to) {
						for (var f in from) {
							if (from.hasOwnProperty(f)) {
								to[f] = from[f];
							}
						}
					},
				
					populateImportNames = function(payload) {
						var importNames = {};
						var reqs = payload.reqs;
						var incs = payload.incs;
						
						fillImportNames(reqs, importNames);
						fillImportNames(incs, importNames);
			
						var blankImportNames = {}
						for (var i in importNames) {
							if (importNames.hasOwnProperty(i) && !importNames[i]) {
								blankImportNames[i] = importNames[i];
							}
						}
						
						myFor : for (var i in blankImportNames) {
							if (blankImportNames.hasOwnProperty(i)) {
								var className = getClassName(i);
								
								for (var name in importNames) {
									if (importNames.hasOwnProperty(name)) {
										if (className === importNames[name]) {
											continue myFor;
										}
									}
								}
								
								for (var name in blankImportNames) {
									if (blankImportNames.hasOwnProperty(name) && name !== i) {
										if (className === getClassName(name)) {
											continue myFor;
										}
									}
								}
								
								importNames[i] = className;
							}
						}
						
						for (name in importNames) {
							if (importNames.hasOwnProperty(name)) {
								if (incs.hasOwnProperty(name)) {
									incs[name] = importNames[name];
								}
								
								if (reqs.hasOwnProperty(name)) {
									reqs[name] = importNames[name];
								}
							}
						}
					},
				
					getClassName = function(fullName) {
						var nameParts = fullName.split(".");
						return nameParts[nameParts.length - 1];
					},
				
					processClassPayload = function(payload) {
				        var clazz = payload.clazz;
				        if (isLoaded(clazz)) {
				        	return;
				        }
				        
						checkDependencies(payload);
						classPayloads[clazz] = payload;
						createPackage(clazz);
						processPayload(payload);
					},
				
					loadClass = function(className, payload) {
						var file = getClassFile(className);
						if (loader.isLoaded(file)) {
				            return;
						}
						
				        loader.load(file, function() { processPayload(payload); });
					},
				
			        loadScript = function(scriptName, payload) {
			        	if (loader.isLoaded(scriptName)) {
			        		return;
			        	}
			        	
			        	scripts[scriptName] = "loading";
			        	
			            loader.load(scriptName, function() {
			            	scripts[scriptName] = "loaded";
			                processPayload(payload);
			            });
			        },
				
					getPayloadId = function(payload) {
						var id = payload.id;
						if (id !== null) {
							return id;
						}
						
			            id = "_" + (payloadCounter++);
			            payload.id = id;
			            payloads[id] = payload;
			            return id;
					},
				
					isScriptPresent = function(s) {
						return loader.isLoaded(s);
					},
					
					isScriptLoaded = function(s) {
						return (scripts.hasOwnProperty(s) && scripts[s] == "loaded") || loader.isLoaded(s);
					},
				
					processPayload = function(payload) {
						var id = getPayloadId(payload);
						
						var incScripts = payload.incScripts;
						var hasIncScriptsLeft = false;
						for (var s in incScripts) {
							if (incScripts.hasOwnProperty(s)) {
								if (isScriptLoaded(s)) {
									continue;
								}
								
								hasIncScriptsLeft = true;
								
								if (!isScriptPresent(s)) {
								   loadScript(s, payload);
								}
							}
						}
						
						var reqScripts = payload.reqScripts;
						var hasReqScriptsLeft = false;
			            for (var s in reqScripts) {
			                if (reqScripts.hasOwnProperty(s)) {
			                	if (isScriptLoaded(s)) {
			                		continue;
			                	}
			                	
			                    hasReqScriptsLeft = true;
			                    if (!isScriptPresent(s)) {
			                    	loadScript(s, payload);
			                    }
			                }
			            }
						
						var incs = payload.incs;
						var hasIncludesLeft = false;
						for (var c in incs) {
							if (incs.hasOwnProperty(c)) {
								if (isLoaded(c)) {
									continue;
								}
								
								hasIncludesLeft = true;
								if (!isPresent(c)) {
									loadClass(c, payload);
								}
							}
						}
						
				        var reqs = payload.reqs;
				        var hasRequiresLeft = false;
						for (var c in reqs) {
				            if (reqs.hasOwnProperty(c)) {
				            	if (isLoaded(c)) {
				            		continue;
				            	}
				            	
			                    hasRequiresLeft = true;
				            	if (!isPresent(c)) {
				    				loadClass(c, payload);
				            	}
							}
						}
						
						if (!(payload.run || hasRequiresLeft || hasReqScriptsLeft)) {
				            runPayload(payload);
						}
						
						if (payloads.hasOwnProperty(id) && !(hasRequiresLeft || hasReqScriptsLeft)) {
							if (!(hasIncludesLeft || hasIncScriptsLeft)) {
				                delete payloads[id];
				                var clazz = payload.clazz;
				                if (clazz) {
				                    delete classPayloads[clazz];
				                    processDependentPayloads(payload);
				                }
				                bindImports(payload);                    
							} else {
								checkIncludeCycle(payload);
							}
						}
					},
				
			        checkIncludeCycle = function(payload) {
			            var checkedClasses = {};
			            var clazz = payload.clazz;
			            if (!(function(payload) {
			                var c = payload.clazz;
			                if (!c) {
			                    return false;
			                }
			                
			                if (!(payload.run)) {
			                    return false;
			                }
			                
			                if (checkedClasses.hasOwnProperty(c)) {
			                    return true;
			                }
			                checkedClasses[c] = payload;
			                
			                var checkLoaded = function(classMap) {
			                    for (var c in classMap) {
			                        if (!classMap.hasOwnProperty(c) || c == clazz) {
			                            continue;
			                        }
			                        
			                        if (isLoaded(c)) {
			                            continue;
			                        }
			                    
			                        var payload = classPayloads[c];
			                        if (payload && arguments.callee.caller(payload)) {
			                            continue;
			                        }
			                        
			                        return false;
			                    }
			                    return true;
			                };
			                
			                if (!checkLoaded(payload.incs)) {
			                    return false;
			                }
			                
			                if (!checkLoaded(payload.reqs)) {
			                    return false;
			                }
			                
			                return true;
			            })(payload)) {
			                return;
			            }
			            
			            for (c in checkedClasses) {
			                if (classPayloads.hasOwnProperty(c)) {
			                    var classPayload = classPayloads[c];
			                    delete classPayloads[c];
			                    delete payloads[classPayloads.id];
			                }
			            }
			            
			            for (c in checkedClasses) {
			                if (checkedClasses.hasOwnProperty(c)) {
			                    var classPayload = checkedClasses[c];
			                    processDependentLoaders(classPayload, checkedClasses);
			                    bindImports(classPayload);
			                }
			            }
			        },
				
					bindImports = function(payload) {
						var imports = payload.imports;
						bindImport(imports, payload.reqs);
						bindImport(imports, payload.incs);
					},
					
					bindImport = function(imports, classMap) {
				        for (var c in classMap) {
				            if (classMap.hasOwnProperty(c)) {
				                var importName = classMap[c];
				                if (importName) {
				                    imports[importName] = getClassReference(c);
				                }
				            }
				        }
					},
				
					processDependentPayloads = function(payload, classMap) {
						for (var cp in classPayloads) {
							if (classPayloads.hasOwnProperty(cp)) {
								var dependentPayload = classPayloads[cp];
								if (dependentPayload != payload) {
			    					processDependentPayload(payload, dependentPayload);
								}
							}
						}
						
						for (var id in payloads) {
				            if (payloads.hasOwnProperty(id)) {
				            	var dependentPayload = payloads[id];
				            	if (dependentPayload != payload) {
			    	                processDependentPayload(payload, payloads[id]);
				            	}
				            }
				    	}
				    	
				    	if (classMap) {
				    		for (c in classMap) {
				                if (classMap.hasOwnProperty(c)) {
			                        var dependentPayload = classMap[c];
			                        if (dependentPayload != payload) {
			    	                    processDependentPayload(payload, dependentPayload);
			                        }
				                }
				    		}
				    	}
					},
				
					processDependentPayload = function(payload, dependentPayload) {
						var clazz = payload.clazz;
						var reference = payload.reference;
						
				        var reqs = dependentPayload.reqs;
				        var incs = dependentPayload.incs;
				        if (reqs.hasOwnProperty(clazz)) {
				            var importName = reqs[clazz];
				            if (importName) {
				                dependentPayload.imports[importName] = reference;
				            }
				        } else if (incs.hasOwnProperty(clazz)) {
				            var importName = incs[clazz];
				            if (importName) {
				                dependentPayload.imports[importName] = reference;
				            }
				        } else {
				            return;
				        }
				        processPayload(dependentPayload);		
					},
				
					runPayload = function(payload) {
						var payloadFunction = payload.payload;
						var clazz = payload.clazz;
						
						if (clazz && !payloadFunction) {
							throw "Cannot provide class \"" + clazz + "\" without a payload function";
						}
						
						if (!payloadFunction) {
							return;
						}
						
			            bindImports(payload);			
			
				        if (payload.run) {
				            return;
				        }
				        
				        payload.run = true;
				        var reference = payloadFunction(payload.imports, scope);
				        
						if (!clazz) {
							return;
						}
						
				        if (typeof reference != "undefined") {
				        	declareClass(clazz, reference);
				        }
				        
				        reference = getClassReference(clazz);
				        if (reference === null) {
							throw "Class \"" + clazz + "\" wasn't declared within payload";
						}
						
						payload.reference = reference;
					},
				
					declareClass = function(clazz, reference) {
						var ref = scope;
						var parts = clazz.split(".");
						for (var i = 0, l = (parts.length - 1);i < l; i++) {
							var p = parts[i];
							ref[p] = ref[p] || {};
							ref = ref[p];
						}
						
						ref[parts[(parts.length - 1)]] = reference;
					},
				
				    isLoading = function(className) {
				    	return classPayloads.hasOwnProperty(className); 
				    },
			    
				    isLoaded = function(className) {
				        return isPresent(className) && !isLoading(className);
				    },
				
					getClassReference = function(className) {
				        var parts = className.split(".");
				        var ref = scope;
				        var hop = Object.prototype.hasOwnProperty;
				        for (var i = 0, l = parts.length; i < l; i++) {
				            var p = parts[i];
				            
				            if (!(hop.call(ref, p))) {
				                return null;
				            }
				            
				            ref = ref[p];
				        }
				        
				        return ref;     
				    },   
			    
					createPackage = function(className) {
						var parts = className.split(".");
						parts.pop();
						var obj = scope;
						for (var i = 0, l = parts.length; i < l; i++) {
							var partName = parts[i];
					        obj[partName] = obj[partName] || {};
					        obj = obj[partName];
						}			
					},
			    
				    isPresent = function(className) {
				    	return getClassReference(className) !== null;
				    };
			    
				declareClass("K", K);	    
			    
				return {
					requires : function(className, importName) {
						return new Loader().requires(className, importName);
					},
					
					requireScript : function(scriptName) {
						return new Loader().requireScript(scriptName);
					},
					
					includeScript : function(scriptName) {
						return new Loader().includeScript(scriptName);
					},
					
					provides : function(className) {
				    	return new Loader().provides(className);
				    },
				    
					includes : function(className, importName) {
				    	return new Loader().includes(className, importName);
				    },
				    
				    basePath : function() {
				    	return basePath;
				    },
				    
				    scope : function() {
				    	return scope;
				    }
				};
			};
			
			var classLoaders = {};
			
			return function(basePath) {
				if (!arguments.length) {
					basePath = defaults.basePath;
				}
				
				if (!classLoaders.hasOwnProperty(basePath)) {
					classLoaders[basePath] = new ClassLoader(basePath)
				}
		
				return classLoaders[basePath];
			};
		}())
	});
	
	
/*	
				requires : function(className, importName) {
					return new Loader().requires(className, importName);
				},
				
				requireScript : function(scriptName) {
					return new Loader().requireScript(scriptName);
				},
				
				includeScript : function(scriptName) {
					return new Loader().includeScript(scriptName);
				},
				
				provides : function(className) {
			    	return new Loader().provides(className);
			    },
			    
				includes : function(className, importName) {
			    	return new Loader().includes(className, importName);
			    },
			    
			    basePath : function() {
			    	return basePath;
			    },
			    
			    scope : function() {
			    	return scope;
			    }
			    
			    */
	

}());